﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FileMonitor.Service.Prototype;
using FileMonitor.Service.ProcessMonitor.Model;
using System.IO;
using FileInfo = FileMonitor.Service.ProcessMonitor.Model.FileInfo;
using System.Threading;

namespace FileMonitor.Service.ProcessMonitor.Manager
{

    public class ProcessStateMonitorManager
    {

        private object fileInfoLockObj;
        private static object commonLockObj=new object();
        private System.Threading.Timer timer;
        private const int pirodTime = 20;

        static ProcessStateMonitorManager instance;

        internal static ProcessStateMonitorManager Current
        {
            get
            {
                if (instance == null)
                {
                    lock (commonLockObj)
                    {
                        if (instance == null)
                        {
                            instance = new ProcessStateMonitorManager();
                        }
                    }
                }
                return instance;
            }
        }

        private Dictionary<string, List<FileInfo>> _allFileInfos;

        private ProcessStateMonitorManager()
        {
            _allFileInfos = new Dictionary<string, List<FileInfo>>();
            foreach (FileMonitorBase process in ProcessManager.Processes)
            {
                List<FileInfo> listFileInfos = new List<FileInfo>();
                _allFileInfos.Add(process.Name, listFileInfos);
            }
            this.fileInfoLockObj = new object();
            timer = new Timer(RemoveDueTimeFileInfo, this, pirodTime, pirodTime);
        }

        private void RemoveDueTimeFileInfo(object state)
        {
            foreach (string key in this._allFileInfos.Keys)
            {
                this._allFileInfos[key].FindAll(fi => (DateTime.Now.Minute - fi.ProcessedDate.Minute) > 20)
                    .ForEach(p => this._allFileInfos[key].Remove(p));
            }
        }

        public void AddNewFile(string fullFilePath, FileMonitorBase process)
        {
            lock (fileInfoLockObj)
            {
                if (!_allFileInfos.ContainsKey(process.Name))
                {
                    throw new KeyNotFoundException("The process not be registered");
                }
                else
                {
                    FileInfo fi = Util.FileUtil.GenerateFileInfo(fullFilePath, FileState.ToBeProcess);
                    if (fi != null)
                    {
                        _allFileInfos[process.Name].Add(fi);
                    }
                }
            }
        }

        public void CompleteFile(string originalFilePath, string newFilePath, FileMonitorBase process)
        {
            lock (fileInfoLockObj)
            {
                if (!_allFileInfos.ContainsKey(process.Name))
                {
                    throw new KeyNotFoundException("The process not be registered");
                }
                else
                {
                    FileInfo fileInfo = _allFileInfos[process.Name].Find(fi => fi.FullPath == originalFilePath);
                    if (fileInfo != null)
                    {
                        fileInfo.FullPath = newFilePath;
                        fileInfo.ProcessedDate = DateTime.Now;
                        fileInfo.State = FileState.Processed;
                    }

                }
            }
        }

        public void LogErrorFile(string originalFilePath, string newFilePath, FileMonitorBase process, string errorMessage)
        {
            lock (fileInfoLockObj)
            {
                if (!_allFileInfos.ContainsKey(process.Name))
                {
                    throw new KeyNotFoundException("The process not be registered");
                }
                else
                {
                    FileInfo fileInfo = _allFileInfos[process.Name].Find(fi => fi.FullPath == originalFilePath);
                    if (fileInfo != null)
                    {
                        fileInfo.FullPath = newFilePath;
                        fileInfo.ProcessedDate = DateTime.Now;
                        fileInfo.State = FileState.Error;
                    }
                }
            }
        }

        public void RemoveFileInfo(string originalFilePath, FileMonitorBase process)
        {
            lock (fileInfoLockObj)
            {

                if (!_allFileInfos.ContainsKey(process.Name))
                {
                    throw new KeyNotFoundException("The process not be registered");
                }
                else
                {
                    string monitorPath = process.MonitorPath;
                    //  File.Move(originalFilePath, Path.Combine(monitorPath, Path.GetFileName(originalFilePath)));
                    if (this._allFileInfos[process.Name].Exists(p => p.FullPath == originalFilePath))
                    {
                        this._allFileInfos.Remove(originalFilePath);
                    }
                }
            }
        }

        public List<FileInfo> GetFileInfos(FileMonitorBase process, FileState fileState)
        {
            lock (fileInfoLockObj)
            {
                switch (fileState)
                {
                    case FileState.AllInProcessBinding:
                        return this._allFileInfos[process.Name].FindAll(fi => fi.State != FileState.AllInProcessBinding);
                        break;
                    case FileState.All:
                        return this._allFileInfos[process.Name];
                        break;
                    case FileState.NonProcessBinding:
                        string tmpPath = Path.Combine(process.MonitorPath, "temp");
                        List<string> files = Directory.GetFiles(tmpPath)
                            .Where(f => (
                                    !f.EndsWith(".error", StringComparison.CurrentCultureIgnoreCase) && (!this._allFileInfos[process.Name].Exists(fi => fi.FullPath == f))
                                )).ToList();
                        List<FileInfo> fileInfos = new List<FileInfo>();
                        files.ForEach(f =>
                        {
                            fileInfos.Add(Util.FileUtil.GenerateFileInfo(f, FileState.NonProcessBinding));
                        });
                        return fileInfos;
                        break;
                    default:
                        return this._allFileInfos[process.Name].FindAll(fi => fi.State == fileState);
                        break;
                }
            }
        }
    }
}
